#include "DeskNotes.h"

BOOL DeskNotes::dwmEnabled;

UINT DeskNotes::WM_CLOSESUBDESKNOTES = 0;
UINT DeskNotes::WM_ACTIVATESUBDESKNOTES = 0;
UINT DeskNotes::WM_DEACTIVATESUBDESKNOTES = 0;


DeskNotes::DeskNotes(bool registerHotkey, tstring title)
{
	width = 300;
	height = 270;
	minWidth = 200;
	minHeight = 200;
	dwmMarginSize = 5;

	if (!WM_CLOSESUBDESKNOTES)
		WM_CLOSESUBDESKNOTES = RegisterWindowMessage(TEXT("{873B4617-3AB3-460D-B761-695B3B36FF05}"));

	if (!WM_ACTIVATESUBDESKNOTES)
		WM_ACTIVATESUBDESKNOTES = RegisterWindowMessage(TEXT("{A8B8DBD5-62F4-4C71-ABD8-95A16474B0AB}"));

	if (!WM_DEACTIVATESUBDESKNOTES)
		WM_DEACTIVATESUBDESKNOTES = RegisterWindowMessage(TEXT("{FF626BAD-6BC0-46AF-8534-5103C36E55EB}"));


	topMost = false;
	transparentOnLeave = true;
	shrunken = false;
	restoredHeight = 0;
	this->registerHotkey = registerHotkey;
	this->title = title;
}

DeskNotes::~DeskNotes()
{
	Deactivate();

	int zoomNumerator = 0;
	int zoomDenominator = 0;
	SendMessage(editHWnd, EM_GETZOOM, (WPARAM)&zoomNumerator, (LPARAM)&zoomDenominator);

	POINT scrollPosition = {0};
	SendMessage(editHWnd, EM_GETSCROLLPOS, 0, (LPARAM)&scrollPosition);

	Settings settings;
	Settings::Data data={0};
	settings.Load(&data);
	data.DeskNotesData.DeskNoteBounds = this->GetBounds();
	data.DeskNotesData.Shrunken = this->shrunken;
	data.DeskNotesData.RestoredHeight = this->restoredHeight;
	data.DeskNotesData.TopMost = this->topMost;
	data.DeskNotesData.TransparentOnLeave = this->transparentOnLeave;
	data.DeskNotesData.ZoomNumerator = zoomNumerator;
	data.DeskNotesData.ZoomDenominator = zoomDenominator;
	data.DeskNotesData.ScrollPosition = scrollPosition;
	settings.Save(data);

	DestroyMenu(systemMenu);
	DestroyMenu(editMenu);

	if (registerHotkey)
		EnumWindows(EnumWindowsProc_Close, (LPARAM)this->GetHWnd());
}

void DeskNotes::Create(HINSTANCE hInstance)
{
	OSVERSIONINFO osvi={0};
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osvi);

	if (osvi.dwMajorVersion < 6)
		dwmEnabled = FALSE;
	else
		DwmIsCompositionEnabled(&dwmEnabled);

	if (dwmEnabled)
	{
		if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) //Windows 8
			roundCornerValue = 0;	
		else
			roundCornerValue = 15;
	}
	else
	{
		roundCornerValue = 0;	
	}

	Settings settings;
	Settings::Data data={0};
	data.DeskNotesData.Shrunken = false;
	data.DeskNotesData.RestoredHeight = 0;
	data.DeskNotesData.TopMost = false;
	data.DeskNotesData.TransparentOnLeave = true;
	data.DeskNotesData.ZoomNumerator = 0;
	data.DeskNotesData.ZoomDenominator = 0;
	settings.Load(&data);

	RECT clientRect = {0,0,width,height};

	int x = CW_USEDEFAULT;
	int y = CW_USEDEFAULT;

	if (RECTWIDTH(data.DeskNotesData.DeskNoteBounds) > 0 && RECTHEIGHT(data.DeskNotesData.DeskNoteBounds) > 0)
	{
		x = data.DeskNotesData.DeskNoteBounds.left;
		y = data.DeskNotesData.DeskNoteBounds.top;
	}

	if (RECTWIDTH(data.DeskNotesData.DeskNoteBounds) > 0 && RECTHEIGHT(data.DeskNotesData.DeskNoteBounds) > 0)
	{
		width = RECTWIDTH(data.DeskNotesData.DeskNoteBounds);
		height = RECTHEIGHT(data.DeskNotesData.DeskNoteBounds);
	}
	else
	{
		AdjustWindowRectEx(&clientRect,WS_CAPTION|WS_THICKFRAME,0,WS_EX_TOOLWINDOW);
		width = RECTWIDTH(clientRect);
		height = RECTHEIGHT(clientRect);
	}

	this->shrunken = data.DeskNotesData.Shrunken;
	this->restoredHeight = data.DeskNotesData.RestoredHeight;
	this->topMost = data.DeskNotesData.TopMost;
	this->transparentOnLeave = data.DeskNotesData.TransparentOnLeave;

	Window::Create(hInstance, TEXT("DeskNotesWindow"), WndProc, WS_EX_LAYERED | WS_EX_TOOLWINDOW, WS_THICKFRAME, CS_VREDRAW | CS_HREDRAW | CS_NOCLOSE, GetStockBrush(BLACK_BRUSH), 0, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)));
	this->SetText(this->title);
	this->SetSize(width, height, true);
	this->SetLocation(x, y);
	this->SetTopMost(this->topMost);

	LoadLibrary(TEXT("Msftedit.dll"));
	editHWnd = CreateWindowEx(0, MSFTEDIT_CLASS, 0, WS_VISIBLE | ES_MULTILINE | WS_CHILD | WS_TABSTOP | ES_AUTOVSCROLL | WS_VSCROLL, dwmMarginSize, dwmMarginSize, width - 2 * dwmMarginSize, height - 2 * dwmMarginSize, this->GetHWnd(), 0, hInstance, 0);

	systemMenu = CreatePopupMenu();
	AppendMenu(systemMenu, topMost ? MF_STRING | MF_CHECKED : MF_STRING, MenuSystem_TopMost,  TEXT("TopMost"));
	AppendMenu(systemMenu,  transparentOnLeave ? MF_STRING | MF_CHECKED : MF_STRING, MenuSystem_TransparentOnLeave, TEXT("Transparent On Leave"));
	AppendMenu(systemMenu, MF_SEPARATOR, MenuSystem_Separator1, 0);
	AppendMenu(systemMenu, MF_STRING, MenuSystem_ShrinkRestore, this->shrunken ? TEXT("Restore") : TEXT("Shrink"));

	if (!registerHotkey)
	{          
		AppendMenu(systemMenu, MF_SEPARATOR, MenuSystem_Separator2, 0);
		AppendMenu(systemMenu, MF_STRING, MenuSystem_Close, TEXT("Close"));
	}

	editMenu = CreatePopupMenu();
	AppendMenu(editMenu, MF_STRING, MenuEdit_Save, TEXT("Save\tCtrl+S"));
	AppendMenu(editMenu, MF_SEPARATOR, MenuEdit_Separator1, 0);
	AppendMenu(editMenu, MF_STRING, MenuEdit_ResetZoom, TEXT("Reset Zoom"));

	//EN_LINK aktivieren
	SendMessage(editHWnd,EM_SETEVENTMASK, 0, ENM_SELCHANGE | ENM_LINK | ENM_KEYEVENTS | ENM_CHANGE | ENM_DRAGDROPDONE);

	//Set the control to automatically detect URLs
	SendMessage(editHWnd, EM_AUTOURLDETECT, TRUE, 0);

	//RTF BGColor
	SendMessage(editHWnd,EM_SETBKGNDCOLOR,0,RGB(255,255,255));

	//Change Font
	CHARFORMAT2 cf;
	cf.cbSize=sizeof(CHARFORMAT2);
	cf.dwMask=CFM_FACE | CFM_SIZE | CFM_BOLD;
	cf.dwEffects = CFE_BOLD;
	cf.yHeight = 220;
	lstrcpy(cf.szFaceName, TEXT("Segoe Print"));
	SendMessage(editHWnd,EM_SETCHARFORMAT,0,(LPARAM)&cf);

	BOOL bl=TRUE;
	DwmSetWindowAttribute(this->GetHWnd(),DWMWA_DISALLOW_PEEK,&bl, sizeof(bl));
	DwmSetWindowAttribute(this->GetHWnd(),DWMWA_EXCLUDED_FROM_PEEK,&bl, sizeof(bl));

	DWMFLIP3DWINDOWPOLICY flip3DPolicy = DWMFLIP3D_EXCLUDEBELOW;
	DwmSetWindowAttribute(this->GetHWnd(), DWMWA_FLIP3D_POLICY, &flip3DPolicy, sizeof(DWMWA_FLIP3D_POLICY));

	Shrink(shrunken);
}


CCState DeskNotes::Activate()
{
	CCState result;
	if ((result = CassiopeiaComponent::Activate()) != CCState::Successful)
		return result;

	//create directory for DeskNotes if necessary
	TCHAR currentDir[_MAX_PATH] = {0};
	TCHAR directory[_MAX_PATH]={0};
	GetCurrentDirectory(sizeof(currentDir),currentDir);
	_stprintf(directory, TEXT("%s\\DeskNotes Archive"), currentDir);
	CreateDirectory(directory, 0);

	MARGINS margins = {dwmMarginSize,dwmMarginSize,dwmMarginSize,dwmMarginSize};
	DwmExtendFrameIntoClientArea(this->GetHWnd(),&margins);

	if (!this->shrunken && this->transparentOnLeave)
		SetLayeredWindowAttributes(this->GetHWnd(), 0, 120, LWA_ALPHA);
	else
		SetLayeredWindowAttributes(this->GetHWnd(), 0, 255, LWA_ALPHA);

	Utilities::MoveWindowIntoVisibleArea(this->GetHWnd());

	this->ShowNoActivate();

	if (!this->topMost)
		SetWindowPos(this->GetHWnd(), Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

	LoadDeskNote();
	LoadRichEditConfiguration();

	if (this->registerHotkey)
		RegisterHotKey(this->GetHWnd(), 1, MOD_WIN, 78);

	SetTimer(this->GetHWnd(),23,1000*60*2,TimerProc);

	if (registerHotkey)
		EnumWindows(EnumWindowsProc_Activate, (LPARAM)this->GetHWnd());

	return CCState::Successful;
}

CCState DeskNotes::Deactivate()
{
	if (!active)
		return CCState::NoActionNecessary;;

	if (!SaveDeskNote())
	{
		if (MessageBox(0,TEXT("Saving DeskNote failed. Continue without saving?"),TEXT("Error"),MB_YESNO) == IDNO)
			return CCState::NotReady;
	}

	if (registerHotkey)
	{
		if (!EnumWindows(EnumWindowsProc_Deactivate, (LPARAM)this->GetHWnd()))
			return CCState::NotReady;
	}

	CCState result;
	if ((result = CassiopeiaComponent::Deactivate()) != CCState::Successful)
		return result;

	this->Hide();
	KillTimer(this->GetHWnd(),23);

	if (this->registerHotkey)
		UnregisterHotKey(this->GetHWnd(), 1);		

	return CCState::Successful;
}


tstring DeskNotes::GetDeskNoteArchiveFileName()
{
	SYSTEMTIME time;
	GetSystemTime(&time);
	TCHAR fileName[_MAX_PATH]={0};
	_stprintf(fileName, TEXT("DeskNotes Archive\\DeskNote_%d-%02d-%02d.rtf"), time.wYear, time.wMonth, time.wDay);
	tstring result(fileName);

	return result;
}

tstring DeskNotes::GetDeskNoteFileName()
{
	return TEXT("DeskNote.rtf");
}

bool DeskNotes::SaveDeskNote()
{
	if (!SaveRichEditToFile(editHWnd,GetDeskNoteArchiveFileName().c_str()))
		return false;

	return SaveRichEditToFile(editHWnd,GetDeskNoteFileName().c_str());
}

bool DeskNotes::LoadDeskNote()
{
	return FillRichEditFromFile(editHWnd, GetDeskNoteFileName().c_str());
}

bool DeskNotes::FillRichEditFromFile(HWND hWnd, LPCTSTR pszFile)
{
	bool fSuccess = false;
	HANDLE hFile = CreateFile(pszFile, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) 
	{
		EDITSTREAM es;
		es.pfnCallback = EditStreamCallbackRead;
		es.dwCookie = (DWORD_PTR)hFile;
		LRESULT lr = SendMessage(hWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
		if (!es.dwError) 
			fSuccess = true;

		CloseHandle(hFile);
	}
	return fSuccess;
}

bool DeskNotes::SaveRichEditToFile(HWND hWnd, LPCTSTR pszFile)
{
	bool fSuccess = false;
	HANDLE hFile = CreateFile(pszFile, GENERIC_WRITE, 0, 0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) 
	{
		EDITSTREAM es;
		es.pfnCallback = EditStreamCallbackWrite;
		es.dwCookie = (DWORD_PTR)hFile;
		LRESULT lr = SendMessage(hWnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
		if (!es.dwError) 
			fSuccess = true;

		CloseHandle(hFile);
	}
	return fSuccess;
}

void DeskNotes::Shrink(bool value)
{
	if (value && !shrunken)
	{
		restoredHeight = GetSize().cy;

		EnableWindow(editHWnd, FALSE);
		this->SetSize(this->GetSize().cx, GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CYFRAME) * 2, true);
		ModifyMenu(systemMenu, MenuSystem_ShrinkRestore, MF_STRING | MF_BYCOMMAND, MenuSystem_ShrinkRestore, TEXT("Restore"));

		shrunken = true;
	}
	else if (!value && shrunken)
	{
		shrunken = false;

		this->SetSize(GetSize().cx, restoredHeight, false);
		EnableWindow(editHWnd, TRUE);
		ModifyMenu(systemMenu, MenuSystem_ShrinkRestore, MF_STRING | MF_BYCOMMAND, MenuSystem_ShrinkRestore, TEXT("Shrink"));
	}

	Utilities::MoveWindowIntoVisibleArea(this->GetHWnd());
}

void DeskNotes::LoadRichEditConfiguration()
{
	Settings settings;
	Settings::Data data={0};
	data.DeskNotesData.ZoomNumerator = 0;
	data.DeskNotesData.ZoomDenominator = 0;
	data.DeskNotesData.ScrollPosition.x = 0;
	data.DeskNotesData.ScrollPosition.y = 0;
	settings.Load(&data);

	SendMessage(editHWnd, EM_SETZOOM, data.DeskNotesData.ZoomNumerator, data.DeskNotesData.ZoomDenominator);
	SendMessage(editHWnd, EM_SETSCROLLPOS, 0, (LPARAM)&data.DeskNotesData.ScrollPosition);
}


DWORD CALLBACK DeskNotes::EditStreamCallbackRead(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
{
	HANDLE hFile = (HANDLE)dwCookie;
	if (ReadFile(hFile, lpBuff, cb, (DWORD *)pcb, 0)) 
	{
		return 0;
	}
	return -1;
}

DWORD CALLBACK DeskNotes::EditStreamCallbackWrite(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
{
	HANDLE hFile = (HANDLE)dwCookie;
	if (WriteFile(hFile, lpBuff, cb, (DWORD *)pcb, 0)) 
	{
		return 0;
	}
	return -1;
}

LRESULT CALLBACK DeskNotes::WndProc(LPVOID sender, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{		
	static RECT windowRectEnterSizeMove;
	static HWND lastActiveHWnd = 0;

	DeskNotes* instance = (DeskNotes*)GetInstance(hWnd);

	//static CHARRANGE tmpChr[2] = {0};

	if (!instance->registerHotkey)
	{
		if (uMsg == WM_CLOSESUBDESKNOTES)
		{
			PostQuitMessage(0);
		}
		else if (uMsg == WM_ACTIVATESUBDESKNOTES)
		{
			instance->Activate();
		}
		else if (uMsg == WM_DEACTIVATESUBDESKNOTES)
		{
			if (instance->Deactivate() != CCState::NotReady)
				return 1;
			else
				return 0;
		}
	}

	switch (uMsg)
	{
		//for saving if richEdit doesn't have the focus
	case WM_KEYDOWN:
		{
			if (wParam == 'S' && GetKeyState(VK_CONTROL) & 0x80)
				instance->SaveDeskNote();

			break;
		}
	case WM_ACTIVATE:
		{
			if (wParam == WA_INACTIVE)
			{
				if (!instance->topMost)
					SetWindowPos(hWnd, Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

				if (!instance->shrunken && instance->transparentOnLeave)
					SetLayeredWindowAttributes(hWnd, 0, 120, LWA_ALPHA);
			}
			else
			{
				SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
			}

			break;
		}
	case WM_DWMCOMPOSITIONCHANGED:
		{
			DwmIsCompositionEnabled(&dwmEnabled);

			if (dwmEnabled)
			{
				instance->roundCornerValue=15;
				MARGINS margins = {instance->dwmMarginSize,instance->dwmMarginSize,instance->dwmMarginSize,instance->dwmMarginSize};
				DwmExtendFrameIntoClientArea(hWnd,&margins);
			}
			else
			{
				instance->roundCornerValue=0;
			}

			break;
		}
	case WM_HOTKEY:
		{
			if (wParam == 1)
			{
				if (GetForegroundWindow() != hWnd)
				{
					lastActiveHWnd = GetForegroundWindow();
					Utilities::MoveWindowIntoVisibleArea(hWnd);
					SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
					SetForegroundWindow(hWnd);
					SetFocus(instance->editHWnd);
				}
				else if (lastActiveHWnd)
				{
					SetForegroundWindow(lastActiveHWnd);
				}
			}

			break;
		}	
	case WM_NOTIFY:
		{		
			static bool selectionChanged = false;
			NMHDR* nmhdr = (NMHDR*)lParam;

			if (nmhdr->code == EN_MSGFILTER)
			{
				MSGFILTER* pMsgFilter = (MSGFILTER*)nmhdr;

				if (pMsgFilter->msg == WM_KEYDOWN)
				{
					if (pMsgFilter->wParam == 'S' && GetKeyState(VK_CONTROL) & 0x80)
					{
						instance->SaveDeskNote();
					}
					else if (GetKeyState(VK_CONTROL) & 0x80 && GetKeyState(VK_SHIFT) & 0x80)
					{
						if (pMsgFilter->wParam == 'D')
						{
							CHARFORMAT2 chf;
							chf.cbSize = sizeof(CHARFORMAT2);

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);

							chf.dwMask = CFM_STRIKEOUT;

							if (chf.dwEffects & CFE_STRIKEOUT)
								chf.dwEffects &= ~CFE_STRIKEOUT;
							else
								chf.dwEffects |= CFE_STRIKEOUT;

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
						}
						else if (pMsgFilter->wParam == 'U')
						{
							CHARFORMAT2 chf;
							chf.cbSize = sizeof(CHARFORMAT2);

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);

							chf.dwMask = CFM_UNDERLINE;

							if (chf.dwEffects & CFE_UNDERLINE)
								chf.dwEffects &= ~CFE_UNDERLINE;
							else
								chf.dwEffects |= CFE_UNDERLINE;

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
						}
						else if (pMsgFilter->wParam == 'M' || pMsgFilter->wParam == '1' || pMsgFilter->wParam == '2')
						{
							CHARFORMAT2 chf;
							chf.cbSize = sizeof(CHARFORMAT2);

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);

							chf.dwMask = CFM_BACKCOLOR;

							if (chf.dwEffects & CFE_AUTOBACKCOLOR)
							{
								switch (pMsgFilter->wParam)
								{
								case '1':
									{
										chf.crBackColor = RGB(255, 93, 54);
										break;
									}
								case '2':
								case 'M':
									{
										chf.crBackColor = RGB(255, 255, 0);
										break;
									}
								}

								//no idea why necessary, but IT IS necessary for setting the background color...
								chf.dwEffects = CFE_BOLD;
							}
							else
							{
								chf.dwEffects = CFE_AUTOBACKCOLOR;
							}

							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
						}
						else if (pMsgFilter->wParam == 'R')
						{
							CHARFORMAT2 chf;
							chf.cbSize = sizeof(CHARFORMAT2);
							chf.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_BOLD | CFM_ITALIC;
							chf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR | CFE_BOLD;
							chf.yHeight = 220;
							lstrcpy(chf.szFaceName, TEXT("Segoe Print"));
							SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
						}					
					}
				}
			} 			
			else if (nmhdr->code == EN_LINK)
			{
				ENLINK* enl = (ENLINK*)nmhdr;
				if (enl->msg == WM_LBUTTONUP)
				{
					if (!selectionChanged)
					{
						TCHAR link[1024] = {0};
						TEXTRANGE textRange = {0};
						textRange.chrg = enl->chrg;
						textRange.lpstrText = link;
						SendMessage(enl->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
						ShellExecute(0, 0, link, 0, 0, SW_SHOWNORMAL);
					}
				}
			} 		
			break;
		}		
	case WM_COMMAND:
		{
			switch (LOWORD(wParam))
			{
			case MenuEdit_Save:
				{
					instance->SaveDeskNote();
					break;
				}
			case MenuEdit_ResetZoom:
				{
					SendMessage(instance->editHWnd, EM_SETZOOM, 0, 0);
					break;
				}
			case MenuSystem_ShrinkRestore:
				{
					instance->Shrink(!instance->shrunken);
					break;
				}
			case MenuSystem_TopMost:
				{
					instance->SetTopMost(!instance->topMost);
					instance->topMost = !instance->topMost;
					CheckMenuItem(instance->systemMenu, MenuSystem_TopMost, instance->topMost ? MF_CHECKED : MF_UNCHECKED);

					break;
				}
			case MenuSystem_TransparentOnLeave:
				{
					instance->transparentOnLeave = !instance->transparentOnLeave;
					CheckMenuItem(instance->systemMenu, MenuSystem_TransparentOnLeave, instance->transparentOnLeave ? MF_CHECKED : MF_UNCHECKED);

					break;
				}
			case MenuSystem_Close:
				{
					if (!instance->registerHotkey)
						PostQuitMessage(0);

					break;
				}
			}

			break;
		}
	case WM_CONTEXTMENU:
		{
			TITLEBARINFO ti = {0};
			ti.cbSize = sizeof(TITLEBARINFO);
			GetTitleBarInfo(hWnd, &ti);

			POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};

			if (mousePos.x == -1 && mousePos.y == -1)
			{
				if (instance->shrunken)
				{
					POINT pos = {instance->GetLocation().x + instance->GetSize().cx / 4, instance->GetLocation().y};
					TrackPopupMenu(instance->systemMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, pos.x, pos.y, 0, hWnd, 0);
				}
				else
				{
					POINT pos = {instance->GetLocation().x + instance->GetSize().cx / 2, instance->GetLocation().y + instance->GetSize().cy / 2};
					TrackPopupMenu(instance->editMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, pos.x, pos.y, 0, hWnd, 0);
				}
			}
			else if (Utilities::RectContainsPoint(ti.rcTitleBar, mousePos))
			{
				TrackPopupMenu(instance->systemMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, mousePos.x, mousePos.y, 0, hWnd, 0);
			}
			else
			{
				TrackPopupMenu(instance->editMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, mousePos.x, mousePos.y, 0, hWnd, 0);
			}
			break;
		}
	case WM_SETCURSOR:
		{
			if (HIWORD(lParam) == WM_MBUTTONDOWN && LOWORD(lParam) == HTCAPTION)
			{
				instance->Shrink(!instance->shrunken);
			}

			if (!instance->shrunken)
				break;

			if (LOWORD(lParam) == HTTOP
				|| LOWORD(lParam) == HTBOTTOM
				|| LOWORD(lParam) == HTTOPLEFT
				|| LOWORD(lParam) == HTBOTTOMLEFT
				|| LOWORD(lParam) == HTLEFT
				|| LOWORD(lParam) == HTRIGHT
				|| LOWORD(lParam) == HTTOPRIGHT
				|| LOWORD(lParam) == HTBOTTOMRIGHT)
			{
				lParam = MAKELPARAM(HTNOWHERE, HIWORD(lParam));
			}

			break;
		}
	case WM_NCLBUTTONDOWN:
		{
			if (!instance->shrunken)
				break;

			if (wParam == HTTOPLEFT
				|| wParam == HTBOTTOMLEFT
				|| wParam ==  HTTOPRIGHT
				|| wParam ==  HTBOTTOMRIGHT
				|| wParam ==  HTBOTTOM
				|| wParam ==  HTTOP
				|| wParam ==  HTLEFT
				|| wParam ==  HTRIGHT)
			{
				wParam = HTCAPTION;
			}

			break;
		}
	case WM_WINDOWPOSCHANGING:
		{
			if (!instance->shrunken)
				break;

			WINDOWPOS* wp = (WINDOWPOS*)lParam;
			wp->cy = GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CYFRAME) * 2;
			lParam = (LPARAM)wp;
			return S_OK;

		}
	case WM_PAINT:
		{
			SolidBrush	 whiteBrush(Color(255, 255, 255));
			Pen			 borderPen(Color(120, 120, 120));
			Pen			 secondaryBorderPen(Color(200, 200, 200));
			HDC          hdc;
			PAINTSTRUCT  ps;
			RECT		 clientRect;

			GetClientRect(hWnd,&clientRect);

			hdc = BeginPaint(hWnd, &ps);

			Graphics graphics(hdc);
			graphics.SetSmoothingMode(SmoothingModeAntiAlias);

			GraphicsPath fillPath;
			GraphicsPath secondaryBorderPath;

			Utilities::CreateRoundRectPath(&fillPath, clientRect, instance->roundCornerValue, -1);
			Utilities::CreateRoundRectPath(&secondaryBorderPath, clientRect, instance->roundCornerValue);

			graphics.FillPath(&whiteBrush,&fillPath);
			graphics.DrawPath(&borderPen,&fillPath);
			graphics.DrawPath(&secondaryBorderPen,&secondaryBorderPath);

			EndPaint(hWnd, &ps);
			break;
		}
	case WM_SIZE:
		{
			RECT clientRect;
			GetClientRect(hWnd,&clientRect);

			SetWindowPos(instance->editHWnd, 0, 0, 0, clientRect.right - clientRect.left - 2 * instance->dwmMarginSize, clientRect.bottom - clientRect.top - 2 * instance->dwmMarginSize, SWP_NOMOVE);

			break;
		}		
	case WM_GETMINMAXINFO:
		{
			if (GetInstance(hWnd))
			{
				MINMAXINFO * mmi = (MINMAXINFO*)lParam;
				mmi->ptMinTrackSize.x = instance->minWidth;
				mmi->ptMinTrackSize.y = instance->minHeight;
			}
			return S_OK;
		}
	case WM_ENTERSIZEMOVE:
		{
			GetWindowRect(hWnd,&windowRectEnterSizeMove);
			SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,0);
			break;
		}
	case WM_EXITSIZEMOVE:
		{
			RECT rect;
			GetWindowRect(hWnd,&rect);

			SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG_PTR)GetStockObject(BLACK_BRUSH));

			if ((rect.right-rect.left) != (windowRectEnterSizeMove.right-windowRectEnterSizeMove.left) || (rect.bottom-rect.top) != (windowRectEnterSizeMove.bottom-windowRectEnterSizeMove.top))
				InvalidateRgn(hWnd,0,true);

			break;
		}
	case WM_DISPLAYCHANGE:
		{
			instance->Refresh();
			break;
		}
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

VOID CALLBACK DeskNotes::TimerProc(HWND hWnd,UINT uMsg,UINT_PTR	idEvent,DWORD dwTime)
{
	((DeskNotes*)GetInstance(hWnd))->SaveDeskNote();
}

BOOL CALLBACK DeskNotes::EnumWindowsProc_Activate(HWND hWnd, LPARAM lParam)
{
	TCHAR className[255]={0};
	GetClassName(hWnd,className,sizeof(className));

	if (hWnd != (HWND)lParam && !_tcscmp(className, TEXT("DeskNotesWindow")))
	{
		PostMessage(hWnd, WM_ACTIVATESUBDESKNOTES, 0, 0);
	}

	return TRUE;
}

BOOL CALLBACK DeskNotes::EnumWindowsProc_Deactivate(HWND hWnd, LPARAM lParam)
{
	TCHAR className[255]={0};
	GetClassName(hWnd,className,sizeof(className));

	if (hWnd != (HWND)lParam && !_tcscmp(className, TEXT("DeskNotesWindow")))
	{
		if (!SendMessage(hWnd, WM_DEACTIVATESUBDESKNOTES, 0, 0))
			return FALSE;
	}

	return TRUE;
}

BOOL CALLBACK DeskNotes::EnumWindowsProc_Close(HWND hWnd, LPARAM lParam)
{
	TCHAR className[255]={0};
	GetClassName(hWnd,className,sizeof(className));

	if (hWnd != (HWND)lParam && !_tcscmp(className, TEXT("DeskNotesWindow")))
	{
		PostMessage(hWnd, WM_CLOSESUBDESKNOTES, 0, 0);
	}

	return TRUE;
}
